#!/bin/bash

# Easy-GPG.sh Copyright (C) 2009  Marco Primi
# This program comes with ABSOLUTELY NO WARRANTY.
# This is free software, and you are welcome to redistribute it under 
# certain conditions; visit http://www.gnu.org/licenses/#GPL for details.


# This script can be used to safely store and organize encrypted files.
# Files can be created for example containing passwords. 
# Later it's easy to search, view and edit the contents of those trough a command-line interface.
# run 'easy-gpg --help' for more informations


### Configurable options ###
# Recipient name (The owner key must be known to gpg already)
dest_user='Marco Primi'
# Editor used for 'new' and edit 'commands'
editor=`which vim`
# The directory where encrypted files are stored
# (Created if it does not exist)
dbdir="$HOME/easy-gpg-db"
# Folder where to store files created with 'new'
# Path is relative to $dbdir, no slashes around!
newfile_dir="uncategorized"
# Folder where to store files imported with 'import'
# Path is relative to $dbdir, no slashes around!
importfile_dir="files"
# Filenames are highlighted when printed by the script
# This defines red text on black background
# To turn this feature off comment the following two variables
COL="\033[0;31m\033[40m"
RESETCOL="\033[0m"


### Auto-detected and private options ###
# Determine for OS-specific commands
OS=`uname -s`
# Find gpg binary
gpg=`which gpg`
# Last update
version="12 July 2010"
# Temp variable used to return a selected file from select_file_in_db
chosen="" 
#####################################################################
function encrypt_file ()
{
 echo "gpg -r " $dest_user "-e " $1
 gpg -r "$dest_user" -e "$1"
} 

function safe_erase_file ()
{ 
  echo "removing temp file $1, press any key to continue";
  read
  case "$OS" in
        Linux)
            shred "$1";
            rm "$1";
            ;;
        Darwin)
            rm -P "$1";
            ;;
        *)
            echo "Unknow system type...";
            echo "Don't know how to erase files safely!"
            rm "$1";
  esac
}

function add_new_file ()
{
  #ask keywords/name
  echo "Insert file name";
  echo "(Hint: use keywords as filename, for example bank_account_x or gmail_address_y or computer_z_password)"
  read NEWFN;

  if [ -e "/tmp/$NEWFN" ]; then 
    echo "Temp file exists, please remove it manually (\"/tmp/$NEWFN\")";
    exit 0;
  fi

  if [ `echo $NEWFN | wc -w` != "1" ]; then
    echo "a filename needs to be a single word, invalid filename \"$NEWFN\"";
    exit 0;
  fi

  #create a new file
  touch "/tmp/$NEWFN" || exit 1;

  #TODO pick a group (subfolders) or create new
  mkdir -p "$dbdir/$newfile_dir" || exit 1;

  if [ -e "/tmp/$NEWFN" ]; then 
    echo "File:$NEWFN" > "/tmp/$NEWFN";
    
    #ask for content using editor
    $editor "/tmp/$NEWFN";
    
    # encrypt
    encrypt_file "/tmp/$NEWFN";
    mv "/tmp/$NEWFN.gpg" "$dbdir/$newfile_dir";
    safe_erase_file "/tmp/$NEWFN";

    if [ -e "$dbdir/$newfile_dir/$NEWFN.gpg" ]; then 
      echo -e "File \"$COL$dbdir/$newfile_dir/$NEWFN.gpg$RESETCOL\" successfully added to db";
      exit 0;
    fi

  else
    echo "Could not create file: /tmp/$NEWFN";
    exit 0;
  fi

  exit 0;
}


function list_dir_recursive () {
    local curr_dir=`basename $1`
    echo "$2+ $curr_dir/"
    
    local files=`find $1 -maxdepth 1 -type f -name "*.gpg" -print`
    for fil in $files; do
        fil=`basename $fil | sed -e s/.gpg//g`
        echo -e "$2|-> $COL$fil$RESETCOL";
    done
    
    local dirs=`find $1 -mindepth 1 -maxdepth 1 -type d -print`
    for dir in $dirs; do
        list_dir_recursive $dir "$2  "
    done

}

function list_all_files
{

  #enumerated (and possibly indented)
  #list of files, then open or quit loop
  echo "Contents of store ($dbdir)"
  list_dir_recursive $dbdir "  ";
  exit 0;
}

function print_help ()
{
  #help
  echo "Usage: $0 {<keyword>|search <keyword>|new|list|import <file>|open <file>.gpg}";

  echo "<keyword>";
  echo "search <keyword>";
  echo "    -> Searches files matching keyword";
  echo "    (shortcuts: s, f, find)";

  echo "new";
  echo "    -> Create and encrypt a text file";

  echo "list";
  echo "    -> List all encrypted files";
  echo "    (shortcuts: l, ls)";

  echo "import <file>";
  echo "    -> Encrypt and import <file> ";

  echo "open <file>.gpg";
  echo "    -> Read encrypted <file>.gpg";
  exit 0;
}

function import_a_file () 
{
  #import an existing file from FS
  # TODO import file
  echo "importing file \"$1\"";
  if [ ! -r $1 ]; then
     echo "Cannot read file $1" 
      exit 0;
  fi
  
  #TODO pick a group (subfolders) or create new
  mkdir -p "$dbdir/$importfile_dir" || exit 1;

  local fname=`basename $1`;

  # encrypt
  encrypt_file "$1";

  if [ ! -e "$1.gpg" ]; then 
    echo "Failed to create $1.gpg";
    exit 1;
  fi
        
  mv -i "$1.gpg" "$dbdir/$importfile_dir/" || rm "$1.gpg";

  if [ -e "$dbdir/$importfile_dir/$fname.gpg" ]; then 
      echo -e "File \"$COL$dbdir/$importfile_dir/$fname.gpg$RESETCOL\" successfully added to db";
      exit 0;
  else
    echo "Could not create file: $dbdir/$importfile_dir/$fname.gpg";
    exit 0;
  fi

  exit 0;
}

function open_gpg_file () 
{
  echo -e "Opening file \"$COL$1$RESETCOL\"";
  echo;

  gpg -d $1;
  exit 0;
}


function select_file_in_db ()
{
  #search with keyword
  echo "searching keyword:\"$1\"";

  MATCHES=`find $dbdir -type f -iname "*$1*.gpg"`

  COUNTER=0
  for i in $MATCHES; do
    let COUNTER=COUNTER+1
  done
  echo "TOT. $COUNTER results";

#no results
  if [ $COUNTER -eq "0" ]; then
    echo "No matches for \"$1\".";
#1 result
  elif [ $COUNTER -eq "1" ]; then
    #echo "The only match is $MATCHES";
    chosen="$MATCHES";
    return 0;
#more than one results
  else
  #print
    SELECTOR=0
    for i in $MATCHES; do
      let SELECTOR=SELECTOR+1
      basenm=`basename $i`
     echo -e " \033[0;31m\033[40m$SELECTOR-->\033[0m $basenm"
    done
    echo "File to open? [1-$COUNTER]";
  #ask user
    read SELECTOR;
  #check bounds
    if (( ("$SELECTOR" >= "1") && ("$SELECTOR" <= "$COUNTER") )); then
  #find and open file
  #TODO creepy code, learn arrays and come back...
    IT=0
    for i in $MATCHES; do
      let IT=IT+1
      if [ "$IT" == "$SELECTOR" ]; then
        chosen="$i";
        return 0;
      fi
    done
  #invalid user input
    else 
      echo "Out of bound: $SELECTOR";
    fi
fi
return 1;
} 

function open_with_keyword () {
  select_file_in_db $1 || exit 0;
  open_gpg_file $chosen;
}

function edit_file () 
{
  #search a file
  select_file_in_db $1 || exit 0;

  echo -e "Editing file \"$COL$chosen$RESETCOL\"";
  echo;  

  # extract
  tmpfile=`mktemp`;
  gpg -d $chosen > $tmpfile;
  
  
  if [ -e "$tmpfile" ]; then 
      
    # edit
    $editor "$tmpfile";
    # replace old file
    encrypt_file "$tmpfile";
    mv -i "$tmpfile.gpg" "$chosen";
    safe_erase_file "$tmpfile";

    if [ -e "$chosen" ]; then 
      echo -e "File \"$COL$chosen$RESETCOL\" successfully edited";
      exit 0;
    fi
  fi

}


function init_checks () {

if [ ! -e "$gpg" ]; then
  echo "gpg is not installed in your system!";
  exit 0;
fi

#Files with spaces in name are a mess to handle
local count;
count=`find $dbdir -iname "* *" | wc -l`;
let 'count+=0';
if [ $count -gt 0 ]; then
    echo "Some file in $dbdir contains spaces in the filename!" 
    echo "Easy-GPG won't start until this is fixed." 
    exit 0;
fi

count=`find $dbdir -iname "*.gpg" | wc -l`;
let 'count+=0';
echo "Easy-GPG.sh ($version!)";
echo "$count files in store"
echo;

}


#####################################################################

init_checks;

case "$1" in
        search | find | s | f)
            open_with_keyword $2
            ;;
        new)
            add_new_file;
            ;;
       edit) 
            edit_file $2;
            ;;         
        list | l | ls)
            list_all_files; 
            ;;
        --help)
            print_help;
            ;;
        import)
            #check that file exists and it's readable
            if [ -r "$2" ]; then
                import_a_file $2;
            else
                echo "Can not read file: \"$2\" ";
                echo;
                echo "Try $0 --help for more info";
            fi
            ;;
         
        open)
            #check that file exists and it's readable
            if [ -r "$2" ]; then
                open_gpg_file $2;
            else
                echo "Can not open file: \"$2\" ";
                echo;
                echo "Try $0 --help for more info";
            fi
            ;;
        "")
            print_help;
            echo
            echo "Please specify a keyword or a command"
            ;;
        *)
            #$1 is a keyword
            open_with_keyword $1 
esac

exit 0;

